#include "devlist.hpp"
#include "common.hpp"
#include <stdio.h>

#define PROC_PARTITIONS "/proc/partitions"
#define DEV "/dev/"

//for stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

//for GET_INDENTITY
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/hdreg.h>

static QString fromIDE(u8 *string)
{
	QString res;
	QByteArray array;
	for(size_t i = 0; i<(strlen((char*)string) - 1)/2; i++){
		u8 *p1 = &string[i*2];
		u8 *p2 = &string[i*2+1];
		array.append(*p2);
		array.append(*p1);
	}
	res = array;
	res = res.trimmed();
	return res;
}

static bool getDeviceInfo(Device &d)
{
	QByteArray ba = d.file.toLocal8Bit();
	char *path = ba.data();

	//stat for size
	struct stat buf;
	if(stat(path, &buf))
		return false;
	d.diskSize = buf.st_size;

	//FS::recognize for fs params
	if(!FS::recognize(d))
		return false;

	//Open disk, we need descriptor for ioctl
	int fdes = open(path, O_RDONLY);
	if(fdes < 0){
		perror("DevList: Device open failed");
		return true;
	}
	
	//try HDIO_GET_IDENTITY
	u8 *serial = NULL, *model = NULL;
	struct hd_driveid ide;
	if(!ioctl(fdes,HDIO_GET_IDENTITY,&ide)){
		serial = ide.serial_no;
		model = ide.model;
	}
	else
		perror("DevList: HDIO_GET_IDENTITY failed");

	//try hard method, copied from hdparm
	if(!serial && !model){
		unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,}; // FIXME?
		struct hd_driveid *id = (struct hd_driveid*)&args[4];
		if (!ioctl(fdes, HDIO_DRIVE_CMD, &args)) {
			serial = id->serial_no;
			model = id->model;
		}
		else
			perror("DevList: HDIO_DRIVE_CMD failed");
	}

	//if we have serial and model, convert it from ata format
	if(serial && model){
		d.diskModel = fromIDE(model);
		d.diskSerial = fromIDE(serial);
	}

	return true;
}

void DevList::scanPartitions()
{
	FILE *procpt;
	char line[0x200], devname[120];
	int major, minor, blocks;

	procpt = fopen(PROC_PARTITIONS, "r");
	if (procpt == NULL) {
		log("DevList: can't open %s \n", PROC_PARTITIONS);
		return;
	}

	//read from /proc
	while (fgets(line, sizeof(line), procpt)) {
		if (sscanf (line, "%d %d %d %s", &major, &minor, &blocks, devname) != 4)
			continue;		
		QString path = QString(DEV) + devname;
		Device d;
		d.file = path;
		if(getDeviceInfo(d))
			devices << d;
	}
	
	fclose(procpt);	
}

#define FIELD(x) case x: return d.x
QVariant DevList::data(const QModelIndex &index, int role) const
{
	if( !inbounds(index.column(), 0, Max) || !inbounds(index.row(), 0, devices.size()) ){
		return QVariant();
	}

	if( (role == Qt::DisplayRole) ){
		Device d = devices[index.row()];
		switch(index.column()){
			FIELD(file);
			FIELD(diskModel);
			FIELD(diskSerial);
			FIELD(diskSize);
			FIELD(fsType);
			FIELD(fsSize);
			FIELD(fsLabel);
		default:
				return QVariant();
		}
	}
	else if (role == Qt::ToolTipRole)
		return devices[index.row()].toString();
	
	return QVariant();
}


static char* names[] = { "file", "diskModel", "diskSerial", "diskSize", "fsType", "fsSize", "fsLabel"};
QVariant DevList::headerData ( int section, Qt::Orientation orientation, int role ) const
{	
	if( (orientation != Qt::Horizontal) 
		|| !inbounds(section, 0, ARRAY_SIZE(names)) 
		|| (role != Qt::DisplayRole))
		return QVariant();
	else
		return names[section];
}

	
DevList::DevList()
{
	scanPartitions();
}
